Conversation
- Wrap application in `ConsentProvider` in `src/index.js`, to ensure the user has consented before analytics is initialized. - Add tracking for key user interactions: - Search performed (`search_performed`) - Metadata view (`metadata_detail_viewed`) - Search result clicks (`search_result_clicked`) - Service URL copied (`service_url_copied`) - Download interactions (`download_link_clicked`, `download_added_to_basket`) - Application button clicks (`application_link_clicked`) - Map interactions (`map_item_added`, `map_item_removed`) - Facet filtering (`facet_filter_applied`)
There was a problem hiding this comment.
Pull request overview
This PR introduces PostHog analytics to the React app, with consent-aware initialization and a set of event captures for key user interactions.
Changes:
- Added a
ConsentProviderwrapper at app bootstrap to manage consent state and initialize/configure PostHog. - Implemented PostHog event tracking across search, metadata views, result clicks, copy/download actions, application links, map interactions, and facet filtering.
- Added PostHog dependencies and environment variables for PostHog key/host.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Adds PostHog (and transitive) dependencies to the lockfile. |
| package.json | Adds @posthog/react and posthog-js dependencies. |
| default.env | Adds REACT_APP_POSTHOG_KEY and REACT_APP_POSTHOG_HOST placeholders. |
| .nvmrc | Updates local Node version to v24. |
| src/utils/consentContext.js | Introduces consent context/provider and PostHog init + opt-in/out behavior. |
| src/index.js | Wraps <App /> with <ConsentProvider />. |
| src/components/routes/OidcCallback.js | Captures a login event and captures exceptions via PostHog on callback failures. |
| src/components/routes/Metadata.js | Captures metadata_detail_viewed when metadata page view tag is pushed. |
| src/components/routes/Home.js | Captures search_performed with search string, counts, and active filters. |
| src/components/partials/SearchResults/MetadataSearchResult.js | Captures result click and service URL copy events. |
| src/components/partials/SearchResults.js | Captures “show more results” click events. |
| src/components/partials/FacetFilter/Facet.js | Captures facet filter applied events including active filters. |
| src/components/partials/Buttons/MapButton.js | Captures map add/remove events. |
| src/components/partials/Buttons/DownloadButton.js | Captures download link click and “added to basket” events. |
| src/components/partials/Buttons/ApplicationButton.js | Captures application link click events. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| navigate(autoRedirectPath); | ||
| } catch (error) { | ||
| console.error("Sign-in response error:", error); | ||
| posthog?.captureException(error); |
There was a problem hiding this comment.
posthog?.captureException(error) can still throw if captureException is undefined on the PostHog client (optional chaining here only guards posthog, not the method). Use optional chaining on the method call (or fall back to capture) to avoid a runtime error during the login callback error path.
| posthog?.captureException(error); | |
| posthog?.captureException?.(error); |
| const selectedFacets = props?.searchData?.selectedFacets || {}; | ||
| const activeFilters = getActiveFiltersFromSelectedFacets(selectedFacets); | ||
|
|
||
| posthog?.capture("facet_filter_applied", { | ||
| facet_field: props.facetField, | ||
| facet_field_name: props.facetFieldNameTranslated, | ||
| facet_name: props.facet.Name, | ||
| facet_name_translated: props.facet.NameTranslated, | ||
| facet_count: props.facet.Count, | ||
| action: checked ? "remove" : "add", | ||
| active_filters: activeFilters, | ||
| }); |
There was a problem hiding this comment.
The facet_filter_applied event is using active_filters derived from the current selectedFacets in props, but the click is about to add/remove the facet via navigation. This means the tracked active_filters will be stale (missing the newly added facet or still including the removed one). Consider computing the next selected facets (add/remove the clicked facet) before capturing, or capturing in a place that runs after searchData.selectedFacets has updated.
| export const getActiveFiltersFromSelectedFacets = (selectedFacets = {}) => { | ||
| const activeFilters = []; | ||
|
|
||
| const flattenFacets = (facets, fieldName) => { | ||
| Object.values(facets || {}).forEach((facet) => { | ||
| activeFilters.push(`${fieldName}: ${facet.NameTranslated || facet.Name}`); | ||
|
|
||
| if (facet.facets && Object.keys(facet.facets).length > 0) { | ||
| flattenFacets(facet.facets, fieldName); | ||
| } | ||
| }); | ||
| }; | ||
|
|
||
| Object.keys(selectedFacets || {}).forEach((fieldKey) => { | ||
| if (selectedFacets[fieldKey]?.facets) { | ||
| flattenFacets(selectedFacets[fieldKey].facets, fieldKey); | ||
| } | ||
| }); | ||
|
|
||
| return activeFilters; | ||
| }; |
There was a problem hiding this comment.
New helper getActiveFiltersFromSelectedFacets is now used for analytics payloads, but there are no unit tests covering its output (including nested facet structures). Since this module already has Jest coverage, please add tests for this function (e.g., flat facets, nested facets, and translated names).
| @@ -1 +1 @@ | |||
| v12.13.1 | |||
| v24 No newline at end of file | |||
There was a problem hiding this comment.
Bumping .nvmrc to Node v24 is likely incompatible with the current tooling stack (react-scripts@5.0.1 / react-app-rewired), and may break local development and CI installs/builds. Please either keep the Node version aligned with what the toolchain supports, or update the build tooling to versions that explicitly support Node 24 and verify in CI.
| v24 | |
| v18 |
ConsentProviderinsrc/index.js, to ensure the user has consented before analytics is initialized.search_performed)metadata_detail_viewed)search_result_clicked)service_url_copied)download_link_clicked,download_added_to_basket)application_link_clicked)map_item_added,map_item_removed)facet_filter_applied)